package com.demo.nien;

import cn.hutool.core.date.DateUtil;
import com.demo.util.Print;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @author lkz
 * @ClassName: ReadWriteLockTest
 * @description: TODO
 * （1）读线程 0、读线程 1、读线程 2 同时获取了读锁，说明可以同时进行共享数据的读操作。
 * （2）写线程 1、写线程 0 只能依次获取写锁，说明共享数据的写操作不能同时进行。
 * （3）读线程 3 必须等待写线程 1 释放写锁后才能获取到读锁，说明读写操作是互斥的。
 * @date 2022/6/20 16:42
 */
public class ReadWriteLockTest {

    //创建一个 Map，代表共享数据
    final static Map<String, String> MAP = new HashMap<String, String>();
    //创建一个读写锁
    final static ReentrantReadWriteLock LOCK = new
            ReentrantReadWriteLock();
    //获取读锁
    final static Lock READ_LOCK = LOCK.readLock();
    //获取写锁
    final static Lock WRITE_LOCK = LOCK.writeLock();
    //对共享数据的写操作
    public static Object put(String key, String value)
    {
        WRITE_LOCK.lock(); //抢写锁
        try
        {
            Print.tco(DateUtil.now()
                    +" 抢占了 WRITE_LOCK，开始执行 write 操作");
            Thread.sleep(1000);
            String put = MAP.put(key, value); //写入共享数据
            return put;
        } catch (Exception e)
        {
            e.printStackTrace();
        } finally
        {
            WRITE_LOCK.unlock(); //释放写锁
        }
        return null;
    }
    //对共享数据的读操作
    public static Object get(String key)
    {
        READ_LOCK.lock(); //抢占读锁
        try
        {
            Print.tco(DateUtil.now()
                    +" 抢占了 READ_LOCK，开始执行 read 操作");
            Thread.sleep(1000);
            String value = MAP.get(key); //读取共享数据
            return value;
        } catch (InterruptedException e)
        {
            e.printStackTrace();
        } finally
        {
            READ_LOCK.unlock(); //释放读锁
        }
        return null;
    }
    //入口方法
    public static void main(String[] args)
    {
        //创建 Runnable 异步可执行目标实例
        Runnable writeTarget = () -> put("key", "value");
        Runnable readTarget = () -> get("key");
        //创建 4 条读线程
        for (int i = 0; i < 4; i++)
        {
            new Thread(readTarget, "读线程" + i).start();
        }
        //创建 2 条写线程，并启动
        for (int i = 0; i < 2; i++)
        {
            new Thread(writeTarget, "写线程" + i).start();
        }
    }

}
